#include <common.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/mtd.h>
#include <asm/gpio.h>
#include <asm/arch/clock.h>
#include <asm/arch/clk.h>
#include <linux/mtd/rawnand.h>
#include <linux/sizes.h>

#define TACLS    					  1				/* 时序相关的设置 			*/
#define TWRPH0   					  4
#define TWRPH1   					  1

struct s5pc1xx_phy_control {
    unsigned int conf;
    unsigned int cont;
    unsigned char cmd;
    unsigned char res1[3];
    unsigned char addr;
    unsigned char res2[3];
    unsigned int data;
    unsigned int eccd1;
    unsigned int eccd2;
    unsigned int eccd;
    unsigned int sblk;
    unsigned int eblk;
    unsigned int stat;
};

#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)

#define SP_OPTIONS NAND_NEED_READRDY
#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16)


/*
 * The chip ID list:
 *    name, device ID, page size, chip size in MiB, eraseblock size, options
 *
 * If page size and eraseblock size are 0, the sizes are taken from the
 * extended chip ID.
 */
struct nand_flash_dev nand_flash_ids[] = {
#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
	LEGACY_ID_NAND("NAND 1MiB 5V 8-bit",	0x6e, 1, SZ_4K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 2MiB 5V 8-bit",	0x64, 2, SZ_4K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit",	0xe8, 1, SZ_4K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 1MiB 3,3V 8-bit",	0xec, 1, SZ_4K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 2MiB 3,3V 8-bit",	0xea, 2, SZ_4K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 	0xd5, 4, SZ_8K, SP_OPTIONS),

	LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit",	0xe6, 8, SZ_8K, SP_OPTIONS),
#endif
	/*
	 * Some incompatible NAND chips share device ID's and so must be
	 * listed by full ID. We list them first so that we can easily identify
	 * the most specific match.
	 */
	{"TC58NVG0S3E 1G 3.3V 8-bit",
		{ .id = {0x98, 0xd1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00} },
		  SZ_2K, SZ_128, SZ_128K, 0, 8, 64, NAND_ECC_INFO(1, SZ_512),
		  2 },
    {"SAMSUNG 1G 3.3V 8-bit",
    { .id = {0xec, 0xd3, 0x51, 0x95, 0x58} },
        SZ_2K, SZ_128, SZ_128K, 0, 5, 64, NAND_ECC_INFO(1, SZ_512)},      
	{"TC58NVG2S0F 4G 3.3V 8-bit",
		{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
		  SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) },
	{"TC58NVG2S0H 4G 3.3V 8-bit",
		{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x16, 0x08, 0x00} },
		  SZ_4K, SZ_512, SZ_256K, 0, 8, 256, NAND_ECC_INFO(8, SZ_512) },
	{"TC58NVG3S0F 8G 3.3V 8-bit",
		{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
		  SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) },
	{"TC58NVG5D2 32G 3.3V 8-bit",
		{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
		  SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
	{"TC58NVG6D2 64G 3.3V 8-bit",
		{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
		  SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
	{"SDTNRGAMA 64G 3.3V 8-bit",
		{ .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} },
		  SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
	{"H27UBG8T2BTR-BC 32G 3.3V 8-bit",
		{ .id = {0xad, 0xd7, 0x94, 0xda, 0x74, 0xc3} },
		  SZ_8K, SZ_4K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
		  NAND_ECC_INFO(40, SZ_1K), 0 },
	{"H27UCG8T2ATR-BC 64G 3.3V 8-bit",
		{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
		  SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
		  NAND_ECC_INFO(40, SZ_1K), 4 },
	{"H27QCG8T2E5R‐BCF 64G 3.3V 8-bit",
		{ .id = {0xad, 0xde, 0x14, 0xa7, 0x42, 0x4a} },
		  SZ_16K, SZ_8K, SZ_4M, NAND_NEED_SCRAMBLING, 6, 1664,
		  NAND_ECC_INFO(56, SZ_1K), 1 },

	LEGACY_ID_NAND("NAND 4MiB 5V 8-bit",   0x6B, 4, SZ_8K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS),

	LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit",  0x33, 16, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit",  0x73, 16, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16),

	LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit",  0x35, 32, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit",  0x75, 32, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16),

	LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit",  0x36, 64, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit",  0x76, 64, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16),

	LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit",  0x78, 128, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit",  0x39, 128, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit",  0x79, 128, SZ_16K, SP_OPTIONS),
	LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16),
	LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16),

	LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS),

	/*
	 * These are the new chips with large page size. Their page size and
	 * eraseblock size are determined from the extended ID bytes.
	 */

	/* 512 Megabit */
	EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit",  0xA2,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit",  0xA0,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit",  0xF2,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit",  0xD0,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit",  0xF0,  64, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2,  64, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0,  64, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2,  64, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0,  64, LP_OPTIONS16),

	/* 1 Gigabit */
	EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit",  0xA1, 128, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit",  0xF1, 128, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit",  0xD1, 128, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16),

	/* 2 Gigabit */
	EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit",  0xAA, 256, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit",  0xDA, 256, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16),

	/* 4 Gigabit */
	EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit",  0xAC, 512, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit",  0xDC, 512, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16),

	/* 8 Gigabit */
	EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit",  0xA3, 1024, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit",  0xD3, 1024, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16),

	/* 16 Gigabit */
	EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit",  0xA5, 2048, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit",  0xD5, 2048, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16),

	/* 32 Gigabit */
	EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit",  0xA7, 4096, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit",  0xD7, 4096, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16),

	/* 64 Gigabit */
	EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit",  0xAE, 8192, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit",  0xDE, 8192, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16),

	/* 128 Gigabit */
	EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit",  0x1A, 16384, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit",  0x3A, 16384, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16),

	/* 256 Gigabit */
	EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit",  0x1C, 32768, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit",  0x3C, 32768, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16),

	/* 512 Gigabit */
	EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit",  0x1E, 65536, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit",  0x3E, 65536, LP_OPTIONS),
	EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16),
	EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16),

	{NULL}
};

static struct nand_chip *chip = NULL;

static uint8_t s5pc1xx_nand_read_byte(struct mtd_info *mtd)
{
    struct nand_chip *nand = mtd->priv;
    struct s5pc1xx_phy_control *ctrl = nand->IO_ADDR_R;
    return ctrl->data  & 0xff;
}

static u16 s5pc1xx_nand_read_word(struct mtd_info *mtd)
{
    u16 data = 0;
    struct nand_chip *nand = mtd->priv;
    struct s5pc1xx_phy_control *ctrl = nand->IO_ADDR_R;
    data = readb(&ctrl->data) << 8;
    data |= readb(&ctrl->data);
    return 0;
}

static void s5pc1xx_nand_write_byte(struct mtd_info *mtd, uint8_t byte)
{
    struct nand_chip *nand = mtd->priv;
    struct s5pc1xx_phy_control *ctrl = nand->IO_ADDR_W;
    writeb(byte, &ctrl->data);
}

static int s5pc1xx_nand_scan_bbt(struct mtd_info *mtd)
{
    return 0;
}

static void s5pc1xx_nand_send_cmd(struct mtd_info *mtd, unsigned command, int column,
			int page_addr)
{
    unsigned long row;
    struct nand_chip *nand = mtd->priv;
    struct s5pc1xx_phy_control *ctrl = nand->IO_ADDR_W;
    row = page_addr / CONFIG_SYS_NAND_PAGE_SIZE;

    writel(command,  &ctrl->cmd);

    writeb(column & 0xff, &ctrl->addr);
    writeb((column >> 8) &0xff, &ctrl->addr);
    writeb(row & 0xff, &ctrl->addr);
    writeb((row >> 8) & 0xff, &ctrl->addr);
    writeb((row >> 16) & 0xff, &ctrl->addr);
}


void nand_gpio_conf(void)
{
    unsigned int val = 0;
    struct gpio_info *gpio_info = get_gpio_data();
    struct s5p_gpio_bank *mp0_1con = (struct s5p_gpio_bank *)(gpio_info->reg_addr + (S5PC110_GPIO_MP010 << 2));
    struct s5p_gpio_bank *mp0_2con = (struct s5p_gpio_bank *)(gpio_info->reg_addr + (S5PC110_GPIO_MP020 << 2));
    struct s5p_gpio_bank *mp0_3con = (struct s5p_gpio_bank *)(gpio_info->reg_addr + (S5PC110_GPIO_MP030 << 2));
    val = readl(mp0_1con);
    val &= ~(0xff << 8);        
    val |= 0x33 << 8;       /* NFCSn1 NFCSn0 */
    writel(val, mp0_1con);
    val = readl(mp0_2con);
    val &= ~(0xff << 8);
    val |= 0x22 << 8;
    writel(val, mp0_2con);
    val = readl(mp0_3con);
    val &= ~0xfffff;
    val |= 0x22222;
    writel(val, mp0_3con);
}

static int s5pc1xx_nand_is_ready(struct mtd_info *mtd)
{
    struct nand_chip *nand = mtd->priv;
    struct s5pc1xx_phy_control *ctrl = nand->IO_ADDR_R;
    return (readl(&ctrl->stat) & 1 << 4);
}

void s5pc1xx_nand_config(struct nand_chip *chip)
{
    struct s5pc1xx_phy_control *ctrl = (struct s5pc1xx_phy_control *)chip->IO_ADDR_W;

    int conf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4) | (0<<3) | (0<<2) | (1<<1)|(0<<0);
    int cont = (0<<18) | (0<<17) | (0<<16) | (0<<10) | (0<<9) | (0<<8) | (0<<7) | (0<<6) | (0x3<<1) | (1<<0);
    writel(conf, &ctrl->conf);
    writel(cont, &ctrl->cont);
}

static void nand_send_cmd(struct nand_chip *nand, unsigned int cmd)
{
    struct s5pc1xx_phy_control *ctrl = nand->IO_ADDR_W;
    writeb(cmd, &ctrl->cmd);
}

static void s5pc1xx_nand_chip_select(struct mtd_info *mtd, int chip)
{
    struct nand_chip *nand = mtd->priv;
    struct s5pc1xx_phy_control *ctrl = nand->IO_ADDR_W;
    unsigned int i = readl(&ctrl->cont);
    if (chip)
        i &= ~(1 << 1);
    else
        i |= (1 << 1);

    writel(i, &ctrl->cont)
        ;
}

void s5pc1xx_nand_reset(struct mtd_info *mtd)
{
    struct nand_chip *nand = mtd->priv;
    s5pc1xx_nand_chip_select(mtd, 1);
    nand_send_cmd(nand, 0xff);

    while(!s5pc1xx_nand_is_ready(mtd))
        ;
    s5pc1xx_nand_chip_select(mtd, 0);
}

int nand_spl_load_image(uint32_t offs, unsigned int uboot_size, void *vdst)
{
    return 1;
}

int nand_deselect(void)
{
    struct nand_chip *nand = chip;
    struct mtd_info *mtd = nand_to_mtd(nand);
    nand->select_chip(mtd, 0);
    return 0;
}

int board_nand_init(struct nand_chip *nand)
{
    struct mtd_info *mtd = nand_to_mtd(nand);

    if (!mtd->priv)
        mtd->priv = nand;

    s5pc1xx_nand_config(nand);
    nand_gpio_conf();
    s5pc1xx_nand_reset(mtd);

    mtd->size = (CONFIG_SYS_NAND_PAGE_SIZE + CONFIG_SYS_NAND_OOBSIZE) * CONFIG_SYS_NAND_PAGE_COUNT;
    mtd->writesize = CONFIG_SYS_NAND_PAGE_SIZE * CONFIG_SYS_NAND_PAGE_COUNT * 1024;
    mtd->erasesize = CONFIG_SYS_NAND_BLOCK_SIZE;
    mtd->ecc_strength = (CONFIG_SYS_NAND_OOBSIZE * CONFIG_SYS_NAND_PAGE_COUNT) * 8;

    nand->read_byte = s5pc1xx_nand_read_byte;
    nand->read_word = s5pc1xx_nand_read_word;
    nand->write_byte = s5pc1xx_nand_write_byte;
    nand->scan_bbt  = s5pc1xx_nand_scan_bbt;
    nand->cmdfunc = s5pc1xx_nand_send_cmd;
    nand->select_chip = s5pc1xx_nand_chip_select;
    nand->dev_ready = s5pc1xx_nand_is_ready;
    chip = nand;
    return 0;
}

struct nand_info {
    uint8_t id[NAND_MAX_ID_LEN];
};

int nand_scan(struct mtd_info *mtd, int maxchips)
{
    struct nand_chip *nand = mtd->priv;
    struct nand_info info = {0};
    int found = 0;
    nand->select_chip(mtd, 1);
    nand->cmdfunc(mtd, NAND_CMD_READID, 0x0, 0x0);

    while(!nand->dev_ready(mtd))
        ;

    for (int i = 0 ; i < NAND_MAX_ID_LEN; i++)
        info.id[i] = nand->read_byte(mtd);

    nand->select_chip(mtd, 0);

    printf("NAND: ");

    for (int i = 0; i < ARRAY_SIZE(nand_flash_ids); i++)
    {
        for (int j = 0; j < nand_flash_ids[i].id_len; j++)
        {
            if (info.id[j] == nand_flash_ids[i].id[j])
            {
                found = i;
                continue;
            }
            else {
                found = 0;
                break;
            }
        }
        if (found)
            break;
    }

    if (found)
        printf("%s\n", nand_flash_ids[found].name);

    return 0;
}
